<template>
  <Modal
    v-bind="curModalOption"
    v-model:open="curVisible"
    @cancel="curVisible = false"
    @ok="onModalOk"
  >
    <Form
      ref="form"
      v-bind="curFormProps"
      :model="curFormData"
      @validate="onValidate"
    >
      <row :gutter="24">
        <template v-for="item in formRules" :key="item.field.toString()">
          <Col
            v-bind="Object.assign({}, col || {}, item.col)"
            v-show="item?.hide ? false : true"
          >
            <form-item v-bind="getItemProps(item)">
              <component
                :is="item.type"
                v-bind="getComProps(item.medelField, item.field, item.props)"
                v-on="getComEvents(item.medelField, item.field, item.on)"
              />
            </form-item>
          </Col>
        </template>
      </row>
    </Form>
  </Modal>
</template>

<script lang="ts" setup>
import { cloneDeep } from 'lodash-es'
import { ref, unref, computed, watch } from 'vue'

import { Modal, Form, Row, Col, FormItem } from 'ant-design-vue'

import type { ColProps } from 'ant-design-vue'
import type { BwFormRuleModel, BwFormGridModel } from './model'

export interface BwModalFormProps {
  visible?: boolean

  title?: string

  okText?: string

  cancelText?: string

  okButtonProps?: Record<string, any>

  width?: number

  minHeight?: number

  maskClosable?: boolean

  hideRequiredMark?: boolean

  col?: ColProps

  wrap?: BwFormGridModel

  formData?: Record<string, any>

  formRules?: Array<BwFormRuleModel>
}

const emit = defineEmits(['update:visible', 'validate', 'ok'])
const props = withDefaults(defineProps<BwModalFormProps>(), {
  visible: false,
  title: '标题',
  width: 1000,
  minHeight: 100,
  okText: '确定',
  cancelText: '取消',

  formData: () => ({}),
  formRules: () => [],
  col: () => ({ span: 24 }),
  wrap: () => ({
    labelCol: { span: 4 },
    wrapperCol: { span: 20 },
  }),
})

const curVisible = computed({
  get: () => props.visible,
  set: (value) => emit('update:visible', value),
})

const curModalOption = computed(() => {
  const options: any = {
    title: props.title,
    width: props.width,
    okText: props.okText,
    cancelText: props.cancelText,
    minHeight: props.minHeight,
    maskClosable: props.maskClosable,
    okButtonProps: props.okButtonProps,
  }
  return options
})

const form = ref()
const curFormData = ref<Record<string, any>>({})
const curFormProps = computed(() => {
  return {
    layout: 'horizontal',
    hideRequiredMark: props.hideRequiredMark,
  }
})

const onValidate = (
  name: string | number | Array<string | number>,
  status: boolean,
  errors: Array<string>
) => {
  emit('validate', name, status, errors)
}

const getItemProps = (item: BwFormRuleModel) => {
  const labelCol = cloneDeep(props.wrap?.labelCol ?? {})
  const wrapperCol = cloneDeep(props.wrap?.wrapperCol ?? {})
  return {
    key: item.field,
    name: item.field,
    label: item.title,
    rules: item.rules,
    labelCol: Object.assign(labelCol, item.labelCol),
    wrapperCol: Object.assign(wrapperCol, item.wrapperCol),
  }
}

const getComEvents = (medelField = 'value', field: string, on: any) => {
  const onEvens: any = { ...on }
  onEvens[`update:${medelField}`] = (value: any) => {
    curFormData.value[field] = value
  }
  return onEvens
}

const getComProps = (
  medelField = 'value',
  field: string,
  props?: Record<string, any>
) => {
  const propData: any = {}
  propData[medelField] = unref(curFormData)[field]
  return Object.assign(propData, props ?? {})
}

const onModalOk = () => {
  unref(form)
    .validate()
    .then(() => {
      emit('ok', unref(curFormData))
    })
}

watch(
  () => props.formData,
  (newVal) => {
    curFormData.value = newVal ?? {}
  },
  { deep: true, immediate: true }
)
</script>

<style lang="less" scoped></style>
